#include "chatservice.hpp"
#include "public.hpp"
#include "muduo/base/Logging.h"

#include <map>
#include <mutex>    // for std::unique_lock<>
#include <iostream> // for std::cerr

using namespace muduo;

// 获取单例对象的静态方法
ChatService *ChatService::getInstance()
{
    // 延迟初始化,实例是在第一次调用 getInstance() 时才创建，而不是在程序启动时就创建
    // 局部静态变量已经是懒加载的方式
    static ChatService service; // 使用局部静态变量保证线程安全（C++11及以上）
    return &service;
}

// 注册消息 以及 对应的业务回调操作
ChatService::ChatService()
    : _userModel(std::make_shared<UserModel>())
    , _offlineMsgModel(std::make_shared<OfflineMsgModel>())
    , _friendModel(std::make_shared<FriendModel>())
    , _groupModel(std::make_shared<GroupModel>())
    , _redis(std::make_unique<Redis>())
    , _connection_count(0)
{
    // 插入处理登陆业务(key = LOGIN_MSG, value = login())
    _msgHandlerMap.insert({LOGIN_MSG, std::bind(&ChatService::login, this,
                                                std::placeholders::_1,
                                                std::placeholders::_2,
                                                std::placeholders::_3)});

    // _msgHandlerMap[LOGIN_MSG] = [this](const muduo::net::TcpConnectionPtr &conn,
    //                                nlohmann::json &js,
    //                                muduo::Timestamp time)
    // {
    //     this->login(conn, js, time);
    // };
    // 插入注册业务(key = REG_MSG, value = reg())
    _msgHandlerMap.insert({REG_MSG, std::bind(&ChatService::reg, this,
                                              std::placeholders::_1,
                                              std::placeholders::_2,
                                              std::placeholders::_3)});

    // 插入一对一聊天业务的(key = ONE_CHAT_MSG, value = oneChat())
    _msgHandlerMap[ONE_CHAT_MSG] = [this](const muduo::net::TcpConnectionPtr &conn,
                                          nlohmann::json &js,
                                          muduo::Timestamp time)
    {
        this->oneChat(conn, js, time);
    };

    // 添加好友业务
    _msgHandlerMap[ADD_FRIEND_MSG] = [this](const muduo::net::TcpConnectionPtr &conn,
                                            nlohmann::json &js,
                                            muduo::Timestamp time)
    {
        this->addFriend(conn, js, time);
    };

    // 注册创建群组业务
    _msgHandlerMap[CREATE_GROUP_MSG] = [this](const muduo::net::TcpConnectionPtr &conn,
                                              nlohmann::json &js,
                                              muduo::Timestamp time)
    {
        this->createGroup(conn, js, time);
    };

    // 加入群组业务
    _msgHandlerMap[ADD_GROUP_MSG] = [this](const muduo::net::TcpConnectionPtr &conn,
                                           nlohmann::json &js,
                                           muduo::Timestamp time)
    {
        this->addGroup(conn, js, time);
    };

    // 群组聊天业务
    _msgHandlerMap[GROUP_CHAT_MSG] = [this](const muduo::net::TcpConnectionPtr &conn,
                                            nlohmann::json &js,
                                            muduo::Timestamp time)
    {
        this->groupChat(conn, js, time);
    };

    // 处理注销业务
    _msgHandlerMap[LOGINOUT_MSG] = [this](const muduo::net::TcpConnectionPtr &conn,
                                          nlohmann::json &js,
                                          muduo::Timestamp time)
    {
        this->loginout(conn, js, time);
    };

    // 在chatservice构造函数过程中， 为redis组件连接redis服务器， 同时设置回调函数handleRedisSubscribeMessage
    if (_redis->connect())
    {
        // 设置上报消息的回调
        _redis->init_notify_handler(std::bind(&ChatService::handleRedisSubscribeMessage, this, std::placeholders::_1, std::placeholders::_2));
    }
    
}

// 处理登陆业务 id pwd pwd
void ChatService::login(const muduo::net::TcpConnectionPtr &conn, nlohmann::json &js, muduo::Timestamp time)
{
    // 1.从 js 对象中获取键为 "id" 的值, key为"password"的值
    int id = js["id"].get<int>();
    std::string pwd = js["password"];

    // 2.根据ID查找用户
    std::unique_ptr<User> user = _userModel->find(id);
    // 3.检查用户的身份验证信息有效
    if (user->getId() == id && user->getPwd() == pwd)
    {
        if (user->getState() == "online")
        {
            // 3.1该用户已经登录， 不允许重复登录
            nlohmann::json response{
                {"msgid", LOGIN_MSG_ACK},
                {"errno", 2},
                {"errmsg", "This account is using, input another!"}};

            // 4.将一个响应对象序列化为 JSON 字符串，然后通过某种连接（conn）发送出去
            conn->send(response.dump());
        }
        else
        {
            // 3.2登录成功 记录用户连接信息
            add_user_connection(id, conn);

            // id用户登陆成功以后, 向redis订阅channel(id)
            _redis->subscribe(id);

            // 3.3用户登录成功 更新用户状态信息state: offline => online
            user->setState("online");
            _userModel->updateState(*user);

            nlohmann::json response{
                {"msgid", LOGIN_MSG_ACK},
                {"errno", 0},
                {"id", user->getId()},
                {"name", user->getName()}};

            // 3.4登录成功： 查询该登录的用户的离线消息
            std::vector<std::string> vec = _offlineMsgModel->query(id);
            if (!vec.empty())
            {
                // 有离线消息就加入到响应json对象中
                response["offlinemsg"] = vec;
                // 读取该用户的离线消息以后， 把该用户的所有离线消息从表中删除
                _offlineMsgModel->remove(id);
            }
            // 查询该登录用户的好友列表
            std::vector<User> userVec = _friendModel->query(id);
            if (!userVec.empty())
            {
                nlohmann::json friendsJson = nlohmann::json::array(); // 创建一个JSON数组
                for (const User &user : userVec)
                {
                    nlohmann::json js;
                    js["id"] = user.getId();
                    js["name"] = user.getName();
                    js["state"] = user.getState();
                    friendsJson.push_back(js); // 直接添加到JSON数组
                }
                // 将JSON数组添加到response
                response["friends"] = friendsJson;
            }

            // 查询用户的群组信息
            std::vector<Group> groupVec = _groupModel->queryGroups(id);
            if (!groupVec.empty())
            {
                // group:[{groupid:[xxx, xxx, xxx, xxx]}]
                std::vector<std::string> groupV;
                // 遍历与id相关的所有Groups
                for (Group &group : groupVec)
                {
                    nlohmann::json groupjson;
                    groupjson["id"] = group.getGId();
                    groupjson["groupname"] = group.getName();
                    groupjson["groupdesc"] = group.getDesc();
                    // 用来存储当前群里的每一个群员
                    std::vector<std::string> userV;
                    // 遍历当前群内的每一个群员
                    for (GroupUser &user : group.getUser())
                    {
                        nlohmann::json userjson;
                        userjson["id"] = user.getId();
                        userjson["name"] = user.getName();
                        userjson["state"] = user.getState();
                        userjson["role"] = user.getRole();
                        userV.push_back(userjson.dump());
                    }
                    // 存储每一个群的群员到users键
                    groupjson["users"] = userV;
                    // 对groupjson群JSON对象序列化后存储到groupV容器中
                    groupV.push_back(groupjson.dump());
                }
                // 将groupjson数组添加到response
                response["groups"] = groupV;
            }
            // 4.将一个响应对象序列化为 JSON 字符串，然后通过某种连接（conn）发送出去
            conn->send(response.dump());
        }
    }
    else
    {
        // 该用户不存在， 或者用户存在且密码错误, 登录失败
        nlohmann::json response{
            {"msgid", LOGIN_MSG_ACK},
            {"errno", 1},
            {"errmsg", "userid or password is invalid!"}};

        // 4.将一个响应对象序列化为 JSON 字符串，然后通过某种连接（conn）发送出去
        conn->send(response.dump());
    }
}

// 登录成功， 记录用户连接信息
void ChatService::add_user_connection(int id, const muduo::net::TcpConnectionPtr &conn)
{
    // 使用 std::unique_lock 获取独占锁
    std::unique_lock<std::shared_mutex> lock(_conn_rw_mutex);
    if (_userConnMap.emplace(id, conn).second == true)
    {                        // 如果插入成功
        ++_connection_count; // 增加连接计数
    }
}

// 删除用户连接信息
void ChatService::remove_user_connection(int id)
{
    // 使用 std::unique_lock 获取独占锁
    std::unique_lock<std::shared_mutex> lock(_conn_rw_mutex);
    if (_userConnMap.erase(id) > 0)
    {                        // 如果删除成功
        --_connection_count; // 减少连接计数
    }
}

// 处理注册业务
void ChatService::reg(const muduo::net::TcpConnectionPtr &conn, nlohmann::json &js, muduo::Timestamp time)
{
    std::string name = js["name"];
    std::string pwd = js["password"];

    // 使用User类 obj
    User user;
    user.setName(name);
    user.setPwd(pwd);
    // 如果 _userModel 还没有被初始化，那么就创建一个新的 UserModel 实例。
    if (!_userModel)
    {
        _userModel = std::make_shared<UserModel>();
    }
    bool state = _userModel->insert(user);
    if (state)
    {
        // 注册成功
        nlohmann::json response{
            {"msgid", REG_MSG_ACK},
            {"errno", 0},
            {"id", user.getId()}};

        // 使用 dump() 函数将 JSON 对象序列化为字符串
        std::string jsonString = response.dump();
        // 通过muduo::net::TcpConnectionPtr对象发送回响
        conn->send(jsonString);
    }
    else
    {
        nlohmann::json response{
            {"msgid", REG_MSG_ACK},
            {"errno", 1}};
        // 使用 dump() 函数将 JSON 对象序列化为字符串
        std::string jsonString = response.dump();
        // 通过muduo::net::TcpConnectionPtr对象发送回响
        conn->send(jsonString);
    }
}

// 一对一聊天业务
void ChatService::oneChat(const muduo::net::TcpConnectionPtr &conn, nlohmann::json &js, muduo::Timestamp time)
{
    try
    {
        // 1.得到对方id
        int receiverId = js.at("receiverId").get<int>(); // 抛出异常如果键不存在

        // 线程安全地访问连接信息表
        std::shared_ptr<muduo::net::TcpConnection> receiverConn;
        {
            std::shared_lock<std::shared_mutex> lock(_conn_rw_mutex);
            // 2.从本地连接映射表中查找对方id对应的conn
            auto it = _userConnMap.find(receiverId);
            if (it != _userConnMap.end())
            {
                receiverConn = it->second;
            }
        }        
        // 3.1如果接收者在线，服务器主动推送消息给接收者
        if (receiverConn)
        {
            receiverConn->send(js.dump());
            return;
        }
        // 3.2 如果用户注册在不同的主机上(服务器集群) ，判断方法就要进一步改进！！！
        // 判断receiverId用户在数据库中的状态是否在线: state == online
        std::unique_ptr<User> user = _userModel->find(receiverId);
        // 3.3如果对方在线并且不是同一台主机上, 则向redis发布消息
        if (user->getState() == "online")
        {
            _redis->publish(receiverId, js.dump());
            return;
        }
        else
        {   // 3.3对方在数据库中状态: state == offline
            // 可选：记录日志或存储离线消息
            _offlineMsgModel->insert(receiverId, js.dump());
            return;
        }
    }
    catch (const nlohmann::json::exception &e)
    {
        // 处理JSON解析错误
        // 记录日志或通知发送方
    }
}

// 添加好友业务  msg = REG_MSG_ACK,  id = 我, friendid = 对方id
void ChatService::addFriend(const muduo::net::TcpConnectionPtr &conn, nlohmann::json &js, muduo::Timestamp time)
{
    // 1.验证用户是否存在
    // {"msgid":6, "id":13, "friendid":14}
    /* +--------+----------+
        | userid | friendid |
        +--------+----------+
        |     13 |       14 |
        +--------+----------+ */
    int userid = js.at("id").get<int>();
    int friendid = js.at("friendid").get<int>();

    // 存储好友信息
    _friendModel->insert(userid, friendid);
}

// 创建群组业务
void ChatService::createGroup(const muduo::net::TcpConnectionPtr &conn, nlohmann::json &js, muduo::Timestamp time)
{
    // 1.创建者ID， 要创建的群名和群信息
    int userid = js.at("id").get<int>();
    std::string name = js.at("groupname");
    std::string desc = js.at("groupdesc");

    // 2.存储新创建的群组信息
    Group group(-1, name, desc);
    // 3.通过智能指针对象操作数据库：创建群组
    if (_groupModel->createGroup(group))
    {
        // 4.存储群组创建人[角色]信息：建立群组和用户之间的关系
        _groupModel->addGroup(userid, group.getGId(), roleToString(Role::CREATOR));
    }
}

// 加入群组业务
void ChatService::addGroup(const muduo::net::TcpConnectionPtr &conn, nlohmann::json &js, muduo::Timestamp time)
{
    int userid = js.at("id").get<int>();
    int groupid = js.at("groupid").get<int>();
    _groupModel->addGroup(userid, groupid, roleToString(Role::NORMAL));
}

// 群组聊天业务 groupid内， 一对多群聊
void ChatService::groupChat(const muduo::net::TcpConnectionPtr &conn, nlohmann::json &js, muduo::Timestamp time)
{
    int userid = js.at("id").get<int>();
    int groupid = js.at("groupid").get<int>();

    // 1. 得到userid所在的群groupid的所有成员，除userid以外
    std::vector<int> useridVec = _groupModel->queryGroupUsers(userid, groupid);

    // 2. 挨个获取成员的TcpConnect并发送消息
    for (const auto &id : useridVec)
    { // 使用auto关键字正确声明循环变量
        {
            std::shared_lock<std::shared_mutex> lock(_conn_rw_mutex); // 将锁移至循环内部，减少锁持有的时间
            auto it = _userConnMap.find(id);
            if (it != _userConnMap.end())
            {
                // 3.在线转发群消息
                it->second->send(js.dump());
            }
            else
            {
                // 3.如果不在线同时查询useridVec::群员id::state == online
                std::unique_ptr<User> user = _userModel->find(id);
                if (user->getState() == "online")
                {   // 群员id在线， 并且在其他主机上登录
                    _redis->publish(id, js.dump());
                }
                else 
                {
                    // 不在线存储离线群消息
                    // 注意：这里没有保持锁，因为_offlineMsgModel插入操作很可能不需要它
                    _offlineMsgModel->insert(id, js.dump());
                }
            }
        } // 锁在此处自动释放
    }
}

// 处理注销业务
void ChatService::loginout(const muduo::net::TcpConnectionPtr &conn, nlohmann::json &js, muduo::Timestamp time)
{
    int userid = js["id"].get<int>();
    {
        std::unique_lock<std::shared_mutex> lock(_conn_rw_mutex);
        auto it = _userConnMap.find(userid);
        if (it != _userConnMap.end())
        {
            // 1.从本地的connectionMap中删除以后
            _userConnMap.erase(it);
            --_connection_count;
        }
    }

    // 2.[redis操作]： 用户注销， 在redis中取消订阅
    _redis->unsubscribe(userid);

    // 3.更新该下线用户的状态信息
    User user(userid, "", "", "offline");
    // 对User表操作
    _userModel->updateState(user);
}

// 获取消息对应的处理器handler
MsgHandler ChatService::getHandler(int msgid)
{
    auto it = _msgHandlerMap.find(msgid);
    if (it != _msgHandlerMap.end())
    {
        return it->second;
    }
    else
    {
        // 如果找不到对应的处理器，返回一个lambda表达式作为默认处理器。
        // 这个lambda会记录错误日志，表明没有为该消息ID注册处理器。
        return [msgid](const muduo::net::TcpConnectionPtr &conn, nlohmann::json &js, muduo::Timestamp time)
        {
            LOG_ERROR << "msgid: " << msgid << " - No handler for message ID";
            // 你可以在这里选择做更多事情，比如发送错误响应给客户端等。
        };
    }
}

// 处理客户端异常关闭
void ChatService::clientCloseException(const std::shared_ptr<TcpConnection> &conn)
{
    int userId = -1; // 初始化为无效值

    {                                                             // 内部做线程安全
        std::unique_lock<std::shared_mutex> lock(_conn_rw_mutex); // 获取独占锁
        for (auto it = _userConnMap.begin(); it != _userConnMap.end();)
        {
            if (it->second == conn)
            {
                userId = it->first;
                auto nextIt = _userConnMap.erase(it);
                --_connection_count; // 删除成功后减少连接计数
                it = nextIt;
                break;
            }
            else
            {
                ++it;
            }
        }
    }

    // [redis操作]: 异常退出也同样取消订阅
    _redis->unsubscribe(userId);

    if (userId != -1)
    { // 如果找到了对应的用户
        User user;
        user.setId(userId);
        user.setState("offline");

        try
        {
            // 2.1更新ORM 对象， 操作更新user表
            _userModel->updateState(user);
        }
        catch (const std::exception &e)
        {
            // 处理或记录异常信息
            std::cerr << "Failed to update user state: " << e.what() << '\n';
        }
    }
}

// 服务器异常， 业务重置方法
void ChatService::resetState()
{
    // 把online => offline
    _userModel->resetState();
}

// 从redis消息队列中获取订阅的消息 ps:这个回调函数由redis在某某场合下调用
// 这个服务器在哪台主机登录，则该主机会向redis6379端口号注册订阅通道
void ChatService::handleRedisSubscribeMessage(const int userid, const std::string message)
{
    std::shared_lock<std::shared_mutex> lock(_conn_rw_mutex);
    auto it = _userConnMap.find(userid);
    if (it != _userConnMap.end())
    {
        // 2.在线转发群消息
        it->second->send(message);
        return;
    }

    // 2.redis在回调上报消息的过程中，用户下线了 ，则存储该用户的离线消息
    _offlineMsgModel->insert(userid, message);
}
